package com.ksyun.ks3.services;

import java.io.File;
import java.security.SignatureException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


import android.content.Context;

import com.ksyun.ks3.auth.AuthUtils;
import com.ksyun.ks3.exception.Ks3ClientException;
import com.ksyun.ks3.exception.Ks3ServiceException;
import com.ksyun.ks3.model.ObjectMetadata;
import com.ksyun.ks3.model.PartETag;
import com.ksyun.ks3.model.PostObjectFormFields;
import com.ksyun.ks3.model.PostPolicy;
import com.ksyun.ks3.model.PostPolicyCondition;
import com.ksyun.ks3.model.acl.AccessControlList;
import com.ksyun.ks3.model.acl.Authorization;
import com.ksyun.ks3.model.acl.CannedAccessControlList;
import com.ksyun.ks3.model.result.AbortMultipartUploadResult;
import com.ksyun.ks3.model.result.CompleteMultipartUploadResult;
import com.ksyun.ks3.model.result.CopyObjectResult;
import com.ksyun.ks3.model.result.CreateBucketResult;
import com.ksyun.ks3.model.result.DeleteBucketResult;
import com.ksyun.ks3.model.result.DeleteObjectResult;
import com.ksyun.ks3.model.result.GetBucketACLResult;
import com.ksyun.ks3.model.result.GetObjectACLResult;
import com.ksyun.ks3.model.result.GetObjectResult;
import com.ksyun.ks3.model.result.HeadBucketResult;
import com.ksyun.ks3.model.result.HeadObjectResult;
import com.ksyun.ks3.model.result.InitiateMultipartUploadResult;
import com.ksyun.ks3.model.result.Ks3SyncResult;
import com.ksyun.ks3.model.result.ListBucketsResult;
import com.ksyun.ks3.model.result.ListObjectsResult;
import com.ksyun.ks3.model.result.ListPartsResult;
import com.ksyun.ks3.model.result.PutBucketACLResult;
import com.ksyun.ks3.model.result.PutObjectACLResult;
import com.ksyun.ks3.model.result.PutObjectResult;
import com.ksyun.ks3.model.result.UploadPartResult;
import com.ksyun.ks3.services.handler.AbortMultipartUploadResponseHandler;
import com.ksyun.ks3.services.handler.AsyncHttpResponseHandler;
import com.ksyun.ks3.services.handler.CompleteMultipartUploadResponseHandler;
import com.ksyun.ks3.services.handler.CopyObjectResponseHandler;
import com.ksyun.ks3.services.handler.CreateBucketResponseHandler;
import com.ksyun.ks3.services.handler.DeleteBucketReplicationResponseHandler;
import com.ksyun.ks3.services.handler.DeleteBucketResponseHandler;
import com.ksyun.ks3.services.handler.DeleteObjectResponseHandler;
import com.ksyun.ks3.services.handler.DeleteObjectTaggingResponseHandler;
import com.ksyun.ks3.services.handler.GetBucketACLResponseHandler;
import com.ksyun.ks3.services.handler.GetBucketReplicationResponseHandler;
import com.ksyun.ks3.services.handler.GetObjectACLResponseHandler;
import com.ksyun.ks3.services.handler.GetObjectResponseHandler;
import com.ksyun.ks3.services.handler.GetObjectTaggingResponseHandler;
import com.ksyun.ks3.services.handler.HeadBucketResponseHandler;
import com.ksyun.ks3.services.handler.HeadObjectResponseHandler;
import com.ksyun.ks3.services.handler.InitiateMultipartUploadResponseHandler;
import com.ksyun.ks3.services.handler.Ks3HttpResponseHandler;
import com.ksyun.ks3.services.handler.ListBucketsResponseHandler;
import com.ksyun.ks3.services.handler.ListObjectsResponseHandler;
import com.ksyun.ks3.services.handler.ListPartsResponseHandler;
import com.ksyun.ks3.services.handler.PutBucketACLResponseHandler;
import com.ksyun.ks3.services.handler.PutBucketReplicationResponseHandler;
import com.ksyun.ks3.services.handler.PutObjectACLResponseHandler;
import com.ksyun.ks3.services.handler.PutObjectFetchResponseHandler;
import com.ksyun.ks3.services.handler.PutObjectResponseHandler;
import com.ksyun.ks3.services.handler.PutObjectTaggingResponseHandler;
import com.ksyun.ks3.services.handler.UploadPartResponseHandler;
import com.ksyun.ks3.services.request.AbortMultipartUploadRequest;
import com.ksyun.ks3.services.request.CompleteMultipartUploadRequest;
import com.ksyun.ks3.services.request.CopyObjectRequest;
import com.ksyun.ks3.services.request.CreateBucketRequest;
import com.ksyun.ks3.services.request.DeleteBucketPolicyRequest;
import com.ksyun.ks3.services.request.DeleteBucketQuotaRequest;
import com.ksyun.ks3.services.request.DeleteBucketReplicationRequest;
import com.ksyun.ks3.services.request.DeleteBucketRequest;
import com.ksyun.ks3.services.request.DeleteObjectRequest;
import com.ksyun.ks3.services.request.GetBucketQuotaRequest;
import com.ksyun.ks3.services.request.PutBucketQuotaRequest;
import com.ksyun.ks3.services.request.PutObjectFetchRequest;
import com.ksyun.ks3.services.request.adp.GetObjectAdpRequest;
import com.ksyun.ks3.services.request.GetBucketACLRequest;
import com.ksyun.ks3.services.request.GetBucketPolicyRequest;
import com.ksyun.ks3.services.request.GetBucketReplicationRequest;
import com.ksyun.ks3.services.request.GetObjectACLRequest;
import com.ksyun.ks3.services.request.GetObjectRequest;
import com.ksyun.ks3.services.request.HeadBucketRequest;
import com.ksyun.ks3.services.request.HeadObjectRequest;
import com.ksyun.ks3.services.request.InitiateMultipartUploadRequest;
import com.ksyun.ks3.services.request.Ks3HttpRequest;
import com.ksyun.ks3.services.request.ListBucketsRequest;
import com.ksyun.ks3.services.request.ListObjectsRequest;
import com.ksyun.ks3.services.request.ListPartsRequest;
import com.ksyun.ks3.services.request.PutBucketPolicyRequest;
import com.ksyun.ks3.services.request.PutBucketACLRequest;
import com.ksyun.ks3.services.request.PutBucketReplicationRequest;
import com.ksyun.ks3.services.request.PutObjectACLRequest;
import com.ksyun.ks3.services.request.PutObjectRequest;
import com.ksyun.ks3.services.request.UploadPartRequest;
import com.ksyun.ks3.services.request.adp.PutObjectAdpRequest;
import com.ksyun.ks3.services.request.PostObjectRequest;
import com.ksyun.ks3.services.request.tag.DeleteObjectTaggingRequest;
import com.ksyun.ks3.services.request.tag.GetObjectTaggingRequest;
import com.ksyun.ks3.services.request.tag.PutObjectTaggingRequest;
import com.ksyun.ks3.util.Base64;
import com.ksyun.ks3.util.ClientIllegalArgumentException;
import com.ksyun.ks3.util.ClientIllegalArgumentExceptionGenerator;
import com.ksyun.ks3.util.Constants;
import com.ksyun.ks3.util.DateUtil;
import com.ksyun.ks3.util.StringUtils;

import org.joda.time.DateTime;

public class Ks3Client implements Ks3 {
    private Ks3ClientConfiguration clientConfiguration;
    private String endpoint;
    public Authorization auth;
    private Ks3HttpExecutor client = new Ks3HttpExecutor();
    private Context context = null;
    public AuthListener authListener = null;

    public Ks3Client(String accesskeyid, String accesskeysecret, Context context) {
        this(accesskeyid, accesskeysecret, Ks3ClientConfiguration
                .getDefaultConfiguration(), context);
    }

    public Ks3Client(String accesskeyid, String accesskeysecret, String securityToken, Context context) {
        this(accesskeyid, accesskeysecret, securityToken, Ks3ClientConfiguration.getDefaultConfiguration(), context);
    }

    public Ks3Client(String accesskeyid, String accesskeysecret,
                     Ks3ClientConfiguration clientConfiguration, Context context) {
        this.auth = new Authorization(accesskeyid, accesskeysecret);
        this.clientConfiguration = clientConfiguration;
        this.context = context;
        //init();
    }

    /**
     * 使用sts形式
     *
     * @param accesskeyid
     * @param accesskeysecret
     * @param securityToken
     * @param clientConfiguration
     * @param context
     */
    public Ks3Client(String accesskeyid, String accesskeysecret, String securityToken,
                     Ks3ClientConfiguration clientConfiguration, Context context) {
        this.auth = new Authorization(accesskeyid, accesskeysecret, securityToken);
        this.clientConfiguration = clientConfiguration;
        this.context = context;
        //init();
    }

    public Ks3Client(Authorization auth, Context context) {
        this(auth, Ks3ClientConfiguration.getDefaultConfiguration(), context);
    }

    public Ks3Client(Authorization auth,
                     Ks3ClientConfiguration clientConfiguration, Context context) {
        this.auth = auth;
        this.clientConfiguration = clientConfiguration;
        this.context = context;
//		init();
    }

    public Ks3Client(AuthListener listener, Context context) {
        this(listener, Ks3ClientConfiguration.getDefaultConfiguration(),
                context);
    }

    public Ks3Client(AuthListener listener,
                     Ks3ClientConfiguration clientConfiguration, Context context) {
        this.authListener = listener;
        this.clientConfiguration = clientConfiguration;
        this.context = context;
//		init();
    }

    public Authorization getAuth() {
        return auth;
    }

    public void setAuth(Authorization auth) {
        this.auth = auth;
    }

//	private void init(String endPoint) {
//		setEndpoint(Constants.ClientConfig_END_POINT);
//	}

    public void setConfiguration(Ks3ClientConfiguration clientConfiguration) {
        this.clientConfiguration = clientConfiguration;
    }

    /**
     * 创建完Ks3Client后应调用此方法设置endpoint;
     * endpoint 取值范围请参考：
     * http://ks3.ksyun.com/doc/api/index.html  --Region（区域）
     *
     * @param endpoint
     */
    public void setEndpoint(String endpoint) {
        this.endpoint = endpoint;
    }

    public AuthListener getAuthListener() {
        return authListener;
    }

    public void setAuthListener(AuthListener authListener) {
        this.authListener = authListener;
    }

    /* Service */
    @Override
    public void listBuckets(ListBucketsResponseHandler resultHandler) {
        listBuckets(new ListBucketsRequest(), resultHandler);
    }

    @Override
    public void listBuckets(ListBucketsRequest request,
                            ListBucketsResponseHandler resultHandler) {
        this.listBuckets(request, resultHandler, true);
    }

    private void listBuckets(ListBucketsRequest request,
                             ListBucketsResponseHandler resultHandler, boolean isUseAsyncMode) {
        invoke(auth, request, resultHandler, isUseAsyncMode);
    }

    /* Bucket ACL */
    @Override
    public void getBucketACL(String bucketName,
                             GetBucketACLResponseHandler resultHandler) {
        this.getBucketACL(new GetBucketACLRequest(bucketName), resultHandler);
    }

    @Override
    public void getBucketACL(GetBucketACLRequest request,
                             GetBucketACLResponseHandler resultHandler) {
        this.getBucketACL(request, resultHandler, true);
    }

    private void getBucketACL(GetBucketACLRequest request,
                              GetBucketACLResponseHandler resultHandler, boolean isUseAsyncMode) {
        this.invoke(auth, request, resultHandler, isUseAsyncMode);
    }

    @Override
    public void putBucketACL(String bucketName,
                             AccessControlList accessControlList,
                             PutBucketACLResponseHandler resultHandler) {
        this.putBucketACL(
                new PutBucketACLRequest(bucketName, accessControlList),
                resultHandler);
    }

    @Override
    public void putBucketACL(String bucketName,
                             CannedAccessControlList CannedAcl,
                             PutBucketACLResponseHandler resultHandler) {
        this.putBucketACL(new PutBucketACLRequest(bucketName, CannedAcl),
                resultHandler);
    }

    @Override
    public void putBucketACL(PutBucketACLRequest request,
                             PutBucketACLResponseHandler resultHandler) {
        this.putBucketACL(request, resultHandler, true);
    }

    private void putBucketACL(PutBucketACLRequest request,
                              PutBucketACLResponseHandler resultHandler, boolean isUseAsyncMode) {
        this.invoke(auth, request, resultHandler, isUseAsyncMode);
    }

    /* Object ACL */
    @Override
    public void putObjectACL(String bucketName, String objectName,
                             CannedAccessControlList accessControlList,
                             PutObjectACLResponseHandler resultHandler) {
        this.putObjectACL(new PutObjectACLRequest(bucketName, objectName,
                accessControlList), resultHandler);
    }

    @Override
    public void putObjectACL(String bucketName, String objectName,
                             AccessControlList accessControlList,
                             PutObjectACLResponseHandler resultHandler) {
        this.putObjectACL(new PutObjectACLRequest(bucketName, objectName,
                accessControlList), resultHandler);
    }

    @Override
    public void putObjectACL(PutObjectACLRequest request,
                             PutObjectACLResponseHandler resultHandler) {
        this.putObjectACL(request, resultHandler, true);
    }

    private void putObjectACL(PutObjectACLRequest request,
                              PutObjectACLResponseHandler resultHandler, boolean isUseAsyncMode) {
        this.invoke(auth, request, resultHandler, isUseAsyncMode);
    }

    @Override
    public void getObjectACL(String bucketName, String ObjectName,
                             GetObjectACLResponseHandler resultHandler) {
        this.getObjectACL(new GetObjectACLRequest(bucketName, ObjectName),
                resultHandler);
    }

    @Override
    public void getObjectACL(GetObjectACLRequest request,
                             GetObjectACLResponseHandler resultHandler) {
        this.getObjectACL(request, resultHandler, true);
    }

    private void getObjectACL(GetObjectACLRequest request,
                              GetObjectACLResponseHandler resultHandler, boolean isUseAsyncMode) {
        this.invoke(auth, request, resultHandler, isUseAsyncMode);
    }

    /* Bucket */
    @Override
    public void headBucket(String bucketname,
                           HeadBucketResponseHandler resultHandler) {
        this.headBucket(new HeadBucketRequest(bucketname), resultHandler);
    }

    @Override
    public void headBucket(HeadBucketRequest request,
                           HeadBucketResponseHandler resultHandler) {
        this.headBucket(request, resultHandler, true);
    }

    private void headBucket(HeadBucketRequest request,
                            HeadBucketResponseHandler resultHandler, boolean isUseAsyncMode) {
        this.invoke(auth, request, resultHandler, isUseAsyncMode);
    }

    @Override
    public boolean bucketExists(String bucketname) {
        return false;
    }

    @Override
    public void createBucket(String bucketname,
                             CreateBucketResponseHandler resultHandler) {
        this.createBucket(new CreateBucketRequest(bucketname), resultHandler);
    }

    @Override
    public void createBucket(String bucketname, AccessControlList list,
                             CreateBucketResponseHandler resultHandler) {
        this.createBucket(new CreateBucketRequest(bucketname, list),
                resultHandler);
    }

    @Override
    public void createBucket(String bucketname, CannedAccessControlList list,
                             CreateBucketResponseHandler resultHandler) {
        this.createBucket(new CreateBucketRequest(bucketname, list),
                resultHandler);
    }

    @Override
    public void createBucket(CreateBucketRequest request,
                             CreateBucketResponseHandler resultHandler) {
        this.createBucket(request, resultHandler, true);
    }

    private void createBucket(CreateBucketRequest request,
                              CreateBucketResponseHandler resultHandler, boolean isUseAsyncMode) {
        invoke(auth, request, resultHandler, isUseAsyncMode);
    }

    @Override
    public void deleteBucket(String bucketname,
                             DeleteBucketResponseHandler resultHandler) {
        this.deleteBucket(new DeleteBucketRequest(bucketname), resultHandler);
    }

    @Override
    public void deleteBucket(DeleteBucketRequest request,
                             DeleteBucketResponseHandler resultHandler) {
        this.deleteBucket(request, resultHandler, true);
    }

    private void deleteBucket(DeleteBucketRequest request,
                              DeleteBucketResponseHandler resultHandler, boolean isUseAsyncMode) {
        invoke(auth, request, resultHandler, isUseAsyncMode);
    }

    /* Object */
    @Override
    public void listObjects(String bucketname,
                            ListObjectsResponseHandler resultHandler) {
        this.listObjects(new ListObjectsRequest(bucketname), resultHandler);
    }

    @Override
    public void listObjects(String bucketname, String prefix,
                            ListObjectsResponseHandler resultHandler) {
        this.listObjects(new ListObjectsRequest(bucketname, prefix),
                resultHandler);
    }

    @Override
    public void listObjects(ListObjectsRequest request,
                            ListObjectsResponseHandler resultHandler) {
        this.listObjects(request, resultHandler, true);
    }

    private void listObjects(ListObjectsRequest request,
                             ListObjectsResponseHandler resultHandler, boolean isUseAsyncMode) {
        this.invoke(auth, request, resultHandler, isUseAsyncMode);
    }

    @Override
    public void deleteObject(String bucketname, String key,
                             DeleteObjectResponseHandler handler) {
        this.deleteObject(new DeleteObjectRequest(bucketname, key), handler);
    }

    @Override
    public void deleteObject(DeleteObjectRequest request,
                             DeleteObjectResponseHandler handler) {
        this.deleteObject(request, handler, true);
    }

    private void deleteObject(DeleteObjectRequest request,
                              DeleteObjectResponseHandler handler, boolean isUseAsyncMode) {
        this.invoke(auth, request, handler, isUseAsyncMode);
    }

    @Override
    public Ks3HttpRequest getObject(Context context, String bucketname,
                                    String key, GetObjectResponseHandler handler) {
        this.context = context;
        return this.getObject(new GetObjectRequest(bucketname, key), handler);
    }

    @Override
    public Ks3HttpRequest getObject(GetObjectRequest request,
                                    GetObjectResponseHandler handler) {
        return this.getObject(request, handler, true);
    }

    private Ks3HttpRequest getObject(GetObjectRequest request,
                                     GetObjectResponseHandler handler, boolean isUseAsyncMode) {
        return this.invoke(auth, request, handler, isUseAsyncMode);
    }

    @Override
    public Ks3HttpRequest putObject(String bucketname, String objectkey,
                                    File file, PutObjectResponseHandler handler) {
        return this.putObject(
                new PutObjectRequest(bucketname, objectkey, file), handler);
    }

    @Override
    public Ks3HttpRequest putObject(String bucketname, String objectkey,
                                    File file, ObjectMetadata objectmeta,
                                    PutObjectResponseHandler handler) {
        return this.putObject(new PutObjectRequest(bucketname, objectkey, file,
                objectmeta), handler);
    }

    @Override
    public Ks3HttpRequest putObject(PutObjectRequest request,
                                    PutObjectResponseHandler handler) {
        return this.putObject(request, handler, true);
    }

    public Ks3HttpRequest putObject(PutObjectRequest request,
                                    PutObjectResponseHandler handler, boolean isUseAsyncMode) {
        return this.invoke(auth, request, handler, isUseAsyncMode);
    }

    @Override
    public void headObject(String bucketname, String objectkey,
                           HeadObjectResponseHandler resultHandler) {
        this.headObject(new HeadObjectRequest(bucketname, objectkey),
                resultHandler);
    }

    @Override
    public void headObject(HeadObjectRequest request,
                           HeadObjectResponseHandler resultHandler) {
        this.headObject(request, resultHandler, true);
    }

    private void headObject(HeadObjectRequest request,
                            HeadObjectResponseHandler resultHandler, boolean isUseAsyncMode) {
        this.invoke(auth, request, resultHandler, isUseAsyncMode);
    }

    @Override
    public void copyObject(String destinationBucket, String destinationObject,
                           String sourceBucket, String sourceKey,
                           CopyObjectResponseHandler handler) {
        CopyObjectRequest request = new CopyObjectRequest(destinationBucket,
                destinationObject, sourceBucket, sourceKey);
        this.copyObject(request, handler);

    }

    @Override
    public void copyObject(String destinationBucket, String destinationObject,
                           String sourceBucket, String sourceKey,
                           CannedAccessControlList cannedAcl, CopyObjectResponseHandler handler) {
        CopyObjectRequest request = new CopyObjectRequest(destinationBucket,
                destinationObject, sourceBucket, sourceKey, cannedAcl);
        this.copyObject(request, handler);

    }

    @Override
    public void copyObject(String destinationBucket, String destinationObject,
                           String sourceBucket, String sourceKey,
                           AccessControlList accessControlList,
                           CopyObjectResponseHandler handler) {

        CopyObjectRequest request = new CopyObjectRequest(destinationBucket,
                destinationObject, sourceBucket, sourceKey, accessControlList);
        this.copyObject(request, handler);

    }

    @Override
    public void copyObject(CopyObjectRequest request,
                           CopyObjectResponseHandler handler) {
        this.copyObject(request, handler, true);
    }

    private void copyObject(CopyObjectRequest request,
                            CopyObjectResponseHandler handler, boolean isUseAsyncMode) {
        this.invoke(auth, request, handler, isUseAsyncMode);
    }

    /* MultiUpload */
    @Override
    public void initiateMultipartUpload(String bucketname, String objectkey,
                                        InitiateMultipartUploadResponseHandler resultHandler) {
        this.initiateMultipartUpload(new InitiateMultipartUploadRequest(
                bucketname, objectkey), resultHandler);
    }

    @Override
    public void initiateMultipartUpload(InitiateMultipartUploadRequest request,
                                        InitiateMultipartUploadResponseHandler resultHandler) {
        this.initiateMultipartUpload(request, resultHandler, true);
    }

    private void initiateMultipartUpload(
            InitiateMultipartUploadRequest request,
            InitiateMultipartUploadResponseHandler resultHandler,
            boolean isUseAsyncMode) {
        this.invoke(auth, request, resultHandler, isUseAsyncMode);
    }

    @Override
    public void uploadPart(String bucketName, String key, String uploadId,
                           File file, long offset, int partNumber, long partSize,
                           UploadPartResponseHandler resultHandler) {
        this.uploadPart(new UploadPartRequest(bucketName, key, uploadId, file,
                offset, partNumber, partSize), resultHandler);
    }

    @Override
    public void uploadPart(UploadPartRequest request,
                           UploadPartResponseHandler resultHandler) {
        this.uploadPart(request, resultHandler, true);
    }

    private void uploadPart(UploadPartRequest request,
                            UploadPartResponseHandler resultHandler, boolean isUseAsyncMode) {
        this.invoke(auth, request, resultHandler, isUseAsyncMode);
    }

    @Override
    public void completeMultipartUpload(String bucketname, String objectkey,
                                        String uploadId, List<PartETag> partETags,
                                        CompleteMultipartUploadResponseHandler handler) {
        this.completeMultipartUpload(new CompleteMultipartUploadRequest(
                bucketname, objectkey, uploadId, partETags), handler);
    }

    @Override
    public void completeMultipartUpload(ListPartsResult result,
                                        CompleteMultipartUploadResponseHandler handler) {
        this.completeMultipartUpload(
                new CompleteMultipartUploadRequest(result), handler);
    }

    @Override
    public void completeMultipartUpload(CompleteMultipartUploadRequest request,
                                        CompleteMultipartUploadResponseHandler handler) {
        this.completeMultipartUpload(request, handler, true);
    }

    private void completeMultipartUpload(
            CompleteMultipartUploadRequest request,
            CompleteMultipartUploadResponseHandler handler,
            boolean isUseAsyncMode) {
        this.invoke(auth, request, handler, isUseAsyncMode);
    }

    @Override
    public void abortMultipartUpload(String bucketname, String objectkey,
                                     String uploadId, AbortMultipartUploadResponseHandler handler) {
        this.abortMultipartUpload(new AbortMultipartUploadRequest(bucketname,
                objectkey, uploadId), handler);
    }

    @Override
    public void abortMultipartUpload(AbortMultipartUploadRequest request,
                                     AbortMultipartUploadResponseHandler handler) {
        this.abortMultipartUpload(request, handler, true);
    }

    private void abortMultipartUpload(AbortMultipartUploadRequest request,
                                      AbortMultipartUploadResponseHandler handler, boolean isUseAsyncMode) {
        this.invoke(auth, request, handler, isUseAsyncMode);
    }

    @Override
    public void listParts(String bucketname, String objectkey, String uploadId,
                          ListPartsResponseHandler handler) {
        this.listParts(new ListPartsRequest(bucketname, objectkey, uploadId),
                handler);
    }

    @Override
    public void listParts(String bucketname, String objectkey, String uploadId,
                          int maxParts, ListPartsResponseHandler handler) {
        this.listParts(new ListPartsRequest(bucketname, objectkey, uploadId,
                maxParts), handler);
    }

    @Override
    public void listParts(String bucketname, String objectkey, String uploadId,
                          int maxParts, int partNumberMarker, ListPartsResponseHandler handler) {
        this.listParts(new ListPartsRequest(bucketname, objectkey, uploadId,
                maxParts, partNumberMarker), handler);
    }

    @Override
    public void listParts(ListPartsRequest request,
                          ListPartsResponseHandler handler) {
        this.listParts(request, handler, true);
    }

    @Override
    public void putBucketCrr(PutBucketReplicationRequest request,
                             PutBucketReplicationResponseHandler handler) {
        this.invoke(auth, request, handler, true);
    }

    @Override
    public void getBucketCrr(GetBucketReplicationRequest request,
                             GetBucketReplicationResponseHandler handler) {
        this.invoke(auth, request, handler, true);
    }

    @Override
    public void deleteBucketCrr(DeleteBucketReplicationRequest request,
                                DeleteBucketReplicationResponseHandler handler) {
        this.invoke(auth, request, handler, true);
    }

    public void putBucketPolicy(PutBucketPolicyRequest request,
                                Ks3HttpResponseHandler handler) {
        this.invoke(auth, request, handler, true);
    }

    public void getBucketPolicy(GetBucketPolicyRequest request,
                                Ks3HttpResponseHandler handler) {
        this.invoke(auth, request, handler, true);
    }

    public void deleteBucketPolicy(DeleteBucketPolicyRequest request,
                                   Ks3HttpResponseHandler handler) {
        this.invoke(auth, request, handler, true);
    }

    public void putBucketQuota(PutBucketQuotaRequest request,
                               Ks3HttpResponseHandler handler) {
        this.invoke(auth, request, handler, true);
    }

    public void getBucketQuota(GetBucketQuotaRequest request,
                               Ks3HttpResponseHandler handler) {
        this.invoke(auth, request, handler, true);
    }

    public void deleteBucketQuota(DeleteBucketQuotaRequest request,
                                  Ks3HttpResponseHandler handler) {
        this.invoke(auth, request, handler, true);
    }

    public void putAdpTask(PutObjectAdpRequest request, Ks3HttpResponseHandler handler) {
        this.invoke(auth, request, handler, true);
    }

    public void getAdpTask(GetObjectAdpRequest request, Ks3HttpResponseHandler handler) {
        this.invoke(auth, request, handler, true);
    }

    public void putObjectTag(PutObjectTaggingRequest request, PutObjectTaggingResponseHandler handler) {
        this.invoke(auth, request, handler, true);
    }

    public void getObjectTag(GetObjectTaggingRequest request, GetObjectTaggingResponseHandler handler) {
        this.invoke(auth, request, handler, true);
    }

    public void deleteObjectTag(DeleteObjectTaggingRequest request, DeleteObjectTaggingResponseHandler handler) {
        this.invoke(auth, request, handler, true);
    }

    public void postObject(PostObjectRequest request, Ks3HttpResponseHandler handler) {
        this.invoke(auth, request, handler, true);
    }

    public void putObjectFetch(PutObjectFetchRequest request, PutObjectFetchResponseHandler handler) {
        this.invoke(auth, request, handler, true);
    }


    private void listParts(ListPartsRequest request,
                           ListPartsResponseHandler handler, boolean isUseAsyncMode) {
        this.invoke(auth, request, handler, isUseAsyncMode);
    }


    /* Invoke asnyc http client */
    private Ks3HttpRequest invoke(Authorization auth, Ks3HttpRequest request,
                                  AsyncHttpResponseHandler resultHandler, boolean isUseAsyncMode) {
        client.invoke(auth, request, resultHandler, clientConfiguration,
                context, endpoint, authListener, isUseAsyncMode);
        return request;
    }

    public PostObjectFormFields getObjectFormFields(String bucket, String filename,
                                                    Map<String, String> postFormData, List<String> unknowValueFormFiled) throws Ks3ClientException {
        if (StringUtils.isBlank(bucket))
            throw ClientIllegalArgumentExceptionGenerator.notNull("bucket");
        if (postFormData == null)
            postFormData = new HashMap<String, String>();
        if (unknowValueFormFiled == null)
            unknowValueFormFiled = new ArrayList<String>();
        postFormData.put("bucket", bucket);
        PostPolicy policy = new PostPolicy();
        //签名将在五小时后过期
        policy.setExpiration(DateUtil.convertDate2Str(new DateTime().plusHours(5).toDate(), DateUtil.DATETIME_PROTOCOL.ISO8861));

        for (Map.Entry<String, String> entry : postFormData.entrySet()) {
            if (!Constants.postFormIgnoreFields.contains(entry.getKey())) {
                PostPolicyCondition condition = new PostPolicyCondition();
                condition.setMatchingType(PostPolicyCondition.MatchingType.eq);
                condition.setParamA("$" + entry.getKey());
                condition.setParamB(entry.getValue().replace("${filename}", filename));
                policy.getConditions().add(condition);
            }
        }
        for (String field : unknowValueFormFiled) {
            if (!Constants.postFormIgnoreFields.contains(field)) {
                PostPolicyCondition condition = new PostPolicyCondition();
                condition.setMatchingType(PostPolicyCondition.MatchingType.startsWith);
                condition.setParamA("$" + field);
                condition.setParamB("");
                policy.getConditions().add(condition);
            }
        }
        return postObject(policy);
    }

    public PostObjectFormFields postObject(PostPolicy policy)
            throws Ks3ClientException {
        Map<String, Object> policyMap = new HashMap<String, Object>();
        policyMap.put("expiration", policy.getExpiration());

        List<List<String>> conditions = new ArrayList<List<String>>();
        for (PostPolicyCondition condition : policy.getConditions()) {
            List<String> conditionList = new ArrayList<String>();
            if (condition.getMatchingType() != PostPolicyCondition.MatchingType.contentLengthRange) {
                if (!condition.getParamA().startsWith("$")) {
                    condition.setParamA("$" + condition.getParamA());
                }
            } else {
                if (!StringUtils.checkLong(condition.getParamA()) || !StringUtils.checkLong(condition.getParamB())) {
                    throw new ClientIllegalArgumentException("contentLengthRange匹配规则的参数A和参数B都应该是Long型");
                }
            }
            conditionList.add(condition.getMatchingType().toString());
            //表单中的项是忽略大小写的
            if (condition.getMatchingType() != PostPolicyCondition.MatchingType.contentLengthRange && !Constants.postFormUnIgnoreCase.contains(condition.getParamA().substring(1))) {
                conditionList.add(condition.getParamA().toLowerCase());
            } else {
                conditionList.add(condition.getParamA());
            }
            conditionList.add(condition.getParamB());
            conditions.add(conditionList);
        }
        policyMap.put("conditions", conditions);
        String policyJson = StringUtils.object2json(policyMap);
        String policyBase64 = "";
        policyBase64 = new String(Base64.encode(policyJson.getBytes()));
        PostObjectFormFields fields = new PostObjectFormFields();
        fields.setKssAccessKeyId(auth.getAccessKeyId());
        fields.setPolicy(policyBase64);
        try {
            fields.setSignature(AuthUtils.calcSignature(auth.getAccessKeySecret(), policyBase64));
        } catch (SignatureException e) {
            throw new Ks3ClientException("计算签名出错", e);
        }
        return fields;
    }

    @Override
    public void pause(Context context) {
    }

    @Override
    public void cancel(Context context) {
    }

    @Override
    public Context getContext() {
        return this.context;
    }

    @Override
    public ListBucketsResult syncListBuckets() throws Ks3ClientException, Ks3ServiceException {
        final Ks3SyncResult<ListBucketsResult> syncResult = new Ks3SyncResult<>();

        this.listBuckets(new ListBucketsRequest(),
                new ListBucketsResponseHandler() {

                    @Override
                    public void onSuccess(ListBucketsRequest request, ListBucketsResult result) {
                        syncResult.setResult(result);
                    }

                    @Override
                    public void onFailure(ListBucketsRequest request, Ks3ClientException clientException, Ks3ServiceException serviceException) {
                        syncResult.setClientException(clientException);
                        syncResult.setServiceException(serviceException);
                    }
                }, false);

        if (syncResult.getClientException() != null) {
            throw syncResult.getClientException();
        }

        if (syncResult.getServiceException() != null) {
            throw syncResult.getServiceException();
        }

        return syncResult.getResult();
    }

    @Override
    public GetBucketACLResult syncGetBucketACL(String bucketName)
            throws Ks3ClientException, Ks3ServiceException {
        GetBucketACLRequest request = new GetBucketACLRequest(bucketName);
        return this.syncGetBucketACL(request);
    }

    @Override
    public GetBucketACLResult syncGetBucketACL(GetBucketACLRequest request)
            throws Ks3ClientException, Ks3ServiceException {

        final Ks3SyncResult<GetBucketACLResult> syncResult = new Ks3SyncResult<>();

        this.getBucketACL(request, new GetBucketACLResponseHandler() {

            @Override
            public void onSuccess(GetBucketACLRequest request, GetBucketACLResult result) {
                syncResult.setResult(result);
            }

            @Override
            public void onFailure(GetBucketACLRequest request, Ks3ClientException clientException, Ks3ServiceException serviceException) {
                syncResult.setClientException(clientException);
                syncResult.setServiceException(serviceException);
            }
        }, false);

        if (syncResult.getClientException() != null) {
            throw syncResult.getClientException();
        }

        if (syncResult.getServiceException() != null) {
            throw syncResult.getServiceException();
        }

        return syncResult.getResult();
    }

    @Override
    public PutBucketACLResult syncPutBucketACL(String bucketName,
                                               AccessControlList accessControlList) throws Ks3ClientException, Ks3ServiceException {
        PutBucketACLRequest request = new PutBucketACLRequest(bucketName,
                accessControlList);
        return this.syncPutBucketACL(request);

    }

    @Override
    public PutBucketACLResult syncPutBucketACL(String bucketName,
                                               CannedAccessControlList accessControlList) throws Ks3ClientException, Ks3ServiceException {
        PutBucketACLRequest request = new PutBucketACLRequest(bucketName,
                accessControlList);
        return this.syncPutBucketACL(request);
    }

    public PutBucketACLResult syncPutBucketACL(PutBucketACLRequest request) throws Ks3ClientException, Ks3ServiceException {
        final Ks3SyncResult<PutBucketACLResult> syncResult = new Ks3SyncResult<>();

        this.putBucketACL(request, new PutBucketACLResponseHandler() {

            @Override
            public void onSuccess(PutBucketACLRequest request, PutBucketACLResult result) {

            }

            @Override
            public void onFailure(PutBucketACLRequest request, Ks3ClientException clientException, Ks3ServiceException serviceException) {
                syncResult.setClientException(clientException);
                syncResult.setServiceException(serviceException);
            }
        }, false);

        if (syncResult.getClientException() != null) {
            throw syncResult.getClientException();
        }

        if (syncResult.getServiceException() != null) {
            throw syncResult.getServiceException();
        }

        return syncResult.getResult();
    }

    @Override
    public PutObjectACLResult syncPutObjectACL(String bucketName, String objectKey,
                                               CannedAccessControlList accessControlList) throws Ks3ClientException, Ks3ServiceException {
        PutObjectACLRequest request = new PutObjectACLRequest(bucketName,
                objectKey, accessControlList);
        return this.syncPutObjectACL(request);
    }

    @Override
    public PutObjectACLResult syncPutObjectACL(String bucketName, String objectKey,
                                               AccessControlList accessControlList) throws Ks3ClientException, Ks3ServiceException {
        PutObjectACLRequest request = new PutObjectACLRequest(bucketName,
                objectKey, accessControlList);
        return this.syncPutObjectACL(request);
    }

    @Override
    public PutObjectACLResult syncPutObjectACL(PutObjectACLRequest request) throws Ks3ClientException, Ks3ServiceException {
        final Ks3SyncResult<PutObjectACLResult> syncResult = new Ks3SyncResult<>();

        this.putObjectACL(request, new PutObjectACLResponseHandler() {

            @Override
            public void onSuccess(PutObjectACLRequest request, PutObjectACLResult result) {
                syncResult.setResult(result);
            }

            @Override
            public void onFailure(PutObjectACLRequest request, Ks3ClientException clientException, Ks3ServiceException serviceException) {
                syncResult.setClientException(clientException);
                syncResult.setServiceException(serviceException);
            }
        }, false);

        if (syncResult.getClientException() != null) {
            throw syncResult.getClientException();
        }

        if (syncResult.getServiceException() != null) {
            throw syncResult.getServiceException();
        }

        return syncResult.getResult();
    }

    @Override
    public GetObjectACLResult syncGetObjectACL(String bucketName,
                                               String objectKey) throws Ks3ClientException, Ks3ServiceException {
        GetObjectACLRequest request = new GetObjectACLRequest(bucketName,
                objectKey);
        return this.syncGetObjectACL(request);
    }

    @Override
    public GetObjectACLResult syncGetObjectACL(GetObjectACLRequest request)
            throws Ks3ClientException, Ks3ServiceException {
        final Ks3SyncResult<GetObjectACLResult> syncResult = new Ks3SyncResult<>();

        this.getObjectACL(request, new GetObjectACLResponseHandler() {

            @Override
            public void onSuccess(GetObjectACLRequest request, GetObjectACLResult result) {
                syncResult.setResult(result);
            }

            @Override
            public void onFailure(GetObjectACLRequest request, Ks3ClientException clientException, Ks3ServiceException serviceException) {
                syncResult.setClientException(clientException);
                syncResult.setServiceException(serviceException);
            }
        }, false);

        if (syncResult.getClientException() != null) {
            throw syncResult.getClientException();
        }

        if (syncResult.getServiceException() != null) {
            throw syncResult.getServiceException();
        }

        return syncResult.getResult();
    }

    @Override
    public HeadBucketResult syncHeadBucket(String bucketName) throws Ks3ClientException, Ks3ServiceException {
        HeadBucketRequest request = new HeadBucketRequest(bucketName);
        return this.syncHeadBucket(request);
    }

    @Override
    public HeadBucketResult syncHeadBucket(HeadBucketRequest request) throws Ks3ClientException, Ks3ServiceException {
        final Ks3SyncResult<HeadBucketResult> syncResult = new Ks3SyncResult<>();

        this.headBucket(request, new HeadBucketResponseHandler() {

            @Override
            public void onSuccess(HeadBucketRequest request, HeadBucketResult result) {
                syncResult.setResult(result);
            }

            @Override
            public void onFailure(HeadBucketRequest request, Ks3ClientException clientException, Ks3ServiceException serviceException) {
                syncResult.setClientException(clientException);
                syncResult.setServiceException(serviceException);
            }
        }, false);

        if (syncResult.getClientException() != null) {
            throw syncResult.getClientException();
        }

        if (syncResult.getServiceException() != null) {
            throw syncResult.getServiceException();
        }

        return syncResult.getResult();
    }

    @Override
    public CreateBucketResult syncCreateBucket(String bucketName) throws Ks3ClientException, Ks3ServiceException {
        CreateBucketRequest request = new CreateBucketRequest(bucketName);
        return this.syncCreateBucket(request);
    }

    @Override
    public CreateBucketResult syncCreateBucket(String bucketName,
                                               CannedAccessControlList accessControlList) throws Ks3ClientException, Ks3ServiceException {
        CreateBucketRequest request = new CreateBucketRequest(bucketName,
                accessControlList);
        return this.syncCreateBucket(request);
    }

    @Override
    public CreateBucketResult syncCreateBucket(String bucketName,
                                               AccessControlList accessControlList) throws Ks3ClientException, Ks3ServiceException {
        CreateBucketRequest request = new CreateBucketRequest(bucketName,
                accessControlList);
        return this.syncCreateBucket(request);
    }

    @Override
    public CreateBucketResult syncCreateBucket(CreateBucketRequest request) throws Ks3ClientException, Ks3ServiceException {

        final Ks3SyncResult<CreateBucketResult> syncResult = new Ks3SyncResult<>();
        this.createBucket(request, new CreateBucketResponseHandler() {

            @Override
            public void onSuccess(CreateBucketRequest request, CreateBucketResult result) {
                syncResult.setResult(result);
            }

            @Override
            public void onFailure(CreateBucketRequest request, Ks3ClientException clientException, Ks3ServiceException serviceException) {
                syncResult.setClientException(clientException);
                syncResult.setServiceException(serviceException);
            }
        }, false);

        if (syncResult.getClientException() != null) {
            throw syncResult.getClientException();
        }

        if (syncResult.getServiceException() != null) {
            throw syncResult.getServiceException();
        }

        return syncResult.getResult();
    }

    @Override
    public DeleteBucketResult syncDeleteBucket(String bucketName) throws Ks3ClientException, Ks3ServiceException {
        DeleteBucketRequest request = new DeleteBucketRequest(bucketName);
        return this.syncDeleteBucket(request);
    }

    @Override
    public DeleteBucketResult syncDeleteBucket(DeleteBucketRequest request) throws Ks3ClientException, Ks3ServiceException {

        final Ks3SyncResult<DeleteBucketResult> syncResult = new Ks3SyncResult<>();
        this.deleteBucket(request, new DeleteBucketResponseHandler() {

            @Override
            public void onSuccess(DeleteBucketRequest request, DeleteBucketResult result) {
                syncResult.setResult(result);
            }

            @Override
            public void onFailure(DeleteBucketRequest request, Ks3ClientException clientException, Ks3ServiceException serviceException) {
                syncResult.setClientException(clientException);
                syncResult.setServiceException(serviceException);
            }
        }, false);

        if (syncResult.getClientException() != null) {
            throw syncResult.getClientException();
        }

        if (syncResult.getServiceException() != null) {
            throw syncResult.getServiceException();
        }

        return syncResult.getResult();
    }

    @Override
    public ListObjectsResult syncListObjects(String bucketName) throws Ks3ClientException, Ks3ServiceException {
        ListObjectsRequest request = new ListObjectsRequest(bucketName);
        return syncListObjects(request);
    }

    @Override
    public ListObjectsResult syncListObjects(String bucketName, String prefix)
            throws Ks3ClientException, Ks3ServiceException {
        ListObjectsRequest request = new ListObjectsRequest(bucketName, prefix);
        return syncListObjects(request);
    }

    @Override
    public ListObjectsResult syncListObjects(ListObjectsRequest request)
            throws Ks3ClientException, Ks3ServiceException {

        final Ks3SyncResult<ListObjectsResult> syncResult = new Ks3SyncResult<>();
        this.listObjects(request, new ListObjectsResponseHandler() {

            @Override
            public void onSuccess(ListObjectsRequest request, ListObjectsResult result) {
                syncResult.setResult(result);
            }

            @Override
            public void onFailure(ListObjectsRequest request, Ks3ClientException clientException, Ks3ServiceException serviceException) {
                syncResult.setClientException(clientException);
                syncResult.setServiceException(serviceException);
            }
        }, false);

        if (syncResult.getClientException() != null) {
            throw syncResult.getClientException();
        }

        if (syncResult.getServiceException() != null) {
            throw syncResult.getServiceException();
        }

        return syncResult.getResult();
    }

    @Override
    public DeleteObjectResult syncDeleteObject(String bucketName, String objectKey)
            throws Ks3ClientException, Ks3ServiceException {
        DeleteObjectRequest request = new DeleteObjectRequest(bucketName,
                objectKey);
        return this.syncDeleteObject(request);
    }

    @Override
    public DeleteObjectResult syncDeleteObject(DeleteObjectRequest request) throws Ks3ClientException, Ks3ServiceException {

        final Ks3SyncResult<DeleteObjectResult> syncResult = new Ks3SyncResult<>();
        this.deleteObject(request, new DeleteObjectResponseHandler() {

            @Override
            public void onSuccess(DeleteObjectRequest request, DeleteObjectResult result) {
                syncResult.setResult(result);
            }

            @Override
            public void onFailure(DeleteObjectRequest request, Ks3ClientException clientException, Ks3ServiceException serviceException) {
                syncResult.setClientException(clientException);
                syncResult.setServiceException(serviceException);
            }
        }, false);

        if (syncResult.getClientException() != null) {
            throw syncResult.getClientException();
        }

        if (syncResult.getServiceException() != null) {
            throw syncResult.getServiceException();
        }

        return syncResult.getResult();
    }

    @Override
    public HeadObjectResult syncHeadObject(String bucketName, String objectKey)
            throws Ks3ClientException, Ks3ServiceException {
        HeadObjectRequest request = new HeadObjectRequest(bucketName, objectKey);
        return this.syncHeadObject(request);
    }

    @Override
    public HeadObjectResult syncHeadObject(HeadObjectRequest request)
            throws Ks3ClientException, Ks3ServiceException {

        final Ks3SyncResult<HeadObjectResult> syncResult = new Ks3SyncResult<>();
        this.headObject(request, new HeadObjectResponseHandler() {

            @Override
            public void onSuccess(HeadObjectRequest request, HeadObjectResult result) {
                syncResult.setResult(result);
            }

            @Override
            public void onFailure(HeadObjectRequest request, Ks3ClientException clientException, Ks3ServiceException serviceException) {
                syncResult.setClientException(clientException);
                syncResult.setServiceException(serviceException);
            }
        }, false);

        if (syncResult.getClientException() != null) {
            throw syncResult.getClientException();
        }

        if (syncResult.getServiceException() != null) {
            throw syncResult.getServiceException();
        }

        return syncResult.getResult();
    }

    @Override
    public CopyObjectResult syncCopyObject(String destinationBucket,
                                           String destinationObject, String sourceBucket, String sourceKey)
            throws Ks3ClientException, Ks3ServiceException {
        CopyObjectRequest request = new CopyObjectRequest(destinationBucket,
                destinationObject, sourceBucket, sourceKey);
        return this.syncCopyObject(request);
    }

    @Override
    public CopyObjectResult syncCopyObject(String destinationBucket,
                                           String destinationObject, String sourceBucket, String sourceKey,
                                           CannedAccessControlList accessControlList) throws Ks3ClientException, Ks3ServiceException {
        CopyObjectRequest request = new CopyObjectRequest(destinationBucket,
                destinationObject, sourceBucket, sourceKey, accessControlList);
        return this.syncCopyObject(request);
    }

    @Override
    public CopyObjectResult syncCopyObject(String destinationBucket,
                                           String destinationObject, String sourceBucket, String sourceKey,
                                           AccessControlList accessControlList) throws Ks3ClientException, Ks3ServiceException {
        CopyObjectRequest request = new CopyObjectRequest(destinationBucket,
                destinationObject, sourceBucket, sourceKey, accessControlList);
        return this.syncCopyObject(request);
    }

    @Override
    public CopyObjectResult syncCopyObject(CopyObjectRequest request)
            throws Ks3ClientException, Ks3ServiceException {

        final Ks3SyncResult<CopyObjectResult> syncResult = new Ks3SyncResult<>();
        this.copyObject(request, new CopyObjectResponseHandler() {

            @Override
            public void onSuccess(CopyObjectRequest request, CopyObjectResult result) {
                syncResult.setResult(result);
            }

            @Override
            public void onFailure(CopyObjectRequest request, Ks3ClientException clientException, Ks3ServiceException serviceException) {
                syncResult.setClientException(clientException);
                syncResult.setServiceException(serviceException);
            }
        }, false);

        if (syncResult.getClientException() != null) {
            throw syncResult.getClientException();
        }

        if (syncResult.getServiceException() != null) {
            throw syncResult.getServiceException();
        }

        return syncResult.getResult();
    }

    @Override
    public InitiateMultipartUploadResult syncInitiateMultipartUpload(
            String bucketName, String objectKey) throws Ks3ClientException, Ks3ServiceException {
        InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(
                bucketName, objectKey);
        return this.syncInitiateMultipartUpload(request);
    }

    @Override
    public InitiateMultipartUploadResult syncInitiateMultipartUpload(
            InitiateMultipartUploadRequest request) throws Ks3ClientException, Ks3ServiceException {

        final Ks3SyncResult<InitiateMultipartUploadResult> syncResult = new Ks3SyncResult<>();
        this.initiateMultipartUpload(request,
                new InitiateMultipartUploadResponseHandler() {

                    @Override
                    public void onSuccess(InitiateMultipartUploadRequest request, InitiateMultipartUploadResult result) {
                        syncResult.setResult(result);
                    }

                    @Override
                    public void onFailure(InitiateMultipartUploadRequest request, Ks3ClientException clientException, Ks3ServiceException serviceException) {
                        syncResult.setClientException(clientException);
                        syncResult.setServiceException(serviceException);
                    }
                }, false);

        if (syncResult.getClientException() != null) {
            throw syncResult.getClientException();
        }

        if (syncResult.getServiceException() != null) {
            throw syncResult.getServiceException();
        }

        return syncResult.getResult();
    }

    @Override
    public CompleteMultipartUploadResult syncCompleteMultipartUpload(
            String bucketName, String objectKey, String uploadId,
            List<PartETag> partETags) throws Ks3ClientException, Ks3ServiceException {
        CompleteMultipartUploadRequest request = new CompleteMultipartUploadRequest(
                bucketName, objectKey, uploadId, partETags);
        return this.syncCompleteMultipartUpload(request);
    }

    @Override
    public CompleteMultipartUploadResult syncCompleteMultipartUpload(
            ListPartsResult result) throws Ks3ClientException, Ks3ServiceException {
        CompleteMultipartUploadRequest request = new CompleteMultipartUploadRequest(
                result);
        return this.syncCompleteMultipartUpload(request);
    }

    @Override
    public CompleteMultipartUploadResult syncCompleteMultipartUpload(
            CompleteMultipartUploadRequest request) throws Ks3ClientException, Ks3ServiceException {

        final Ks3SyncResult<CompleteMultipartUploadResult> syncResult = new Ks3SyncResult<>();
        this.completeMultipartUpload(request,
                new CompleteMultipartUploadResponseHandler() {

                    @Override
                    public void onSuccess(CompleteMultipartUploadRequest request, CompleteMultipartUploadResult result) {
                        syncResult.setResult(result);
                    }

                    @Override
                    public void onFailure(CompleteMultipartUploadRequest request, Ks3ClientException clientException, Ks3ServiceException serviceException) {
                        syncResult.setClientException(clientException);
                        syncResult.setServiceException(serviceException);
                    }
                }, false);

        if (syncResult.getClientException() != null) {
            throw syncResult.getClientException();
        }

        if (syncResult.getServiceException() != null) {
            throw syncResult.getServiceException();
        }

        return syncResult.getResult();
    }

    @Override
    public AbortMultipartUploadResult syncAbortMultipartUpload(AbortMultipartUploadRequest request)
            throws Ks3ClientException, Ks3ServiceException {

        final Ks3SyncResult<AbortMultipartUploadResult> syncResult = new Ks3SyncResult<>();

        this.abortMultipartUpload(request,
                new AbortMultipartUploadResponseHandler() {

                    @Override
                    public void onSuccess(AbortMultipartUploadRequest request, AbortMultipartUploadResult result) {
                        syncResult.setResult(result);
                    }

                    @Override
                    public void onFailure(AbortMultipartUploadRequest request, Ks3ClientException clientException, Ks3ServiceException serviceException) {
                        syncResult.setClientException(clientException);
                        syncResult.setServiceException(serviceException);
                    }
                }, false);

        if (syncResult.getClientException() != null) {
            throw syncResult.getClientException();
        }

        if (syncResult.getServiceException() != null) {
            throw syncResult.getServiceException();
        }

        return syncResult.getResult();
    }

    @Override
    public AbortMultipartUploadResult syncAbortMultipartUpload(String bucketname, String objectKey,
                                                               String uploadId) throws Ks3ClientException, Ks3ServiceException {
        AbortMultipartUploadRequest request = new AbortMultipartUploadRequest(
                bucketname, objectKey, uploadId);
        return this.syncAbortMultipartUpload(request);
    }

    @Override
    public ListPartsResult syncListParts(String bucketName, String objectKey,
                                         String uploadId) throws Ks3ClientException, Ks3ServiceException {
        ListPartsRequest request = new ListPartsRequest(bucketName, objectKey,
                uploadId);
        return this.syncListParts(request);
    }

    @Override
    public ListPartsResult syncListParts(String bucketName, String objectKey,
                                         String uploadId, int maxParts) throws Ks3ClientException, Ks3ServiceException {
        ListPartsRequest request = new ListPartsRequest(bucketName, objectKey,
                uploadId, maxParts);
        return this.syncListParts(request);
    }

    @Override
    public ListPartsResult syncListParts(String bucketName, String objectKey,
                                         String uploadId, int maxParts, int partNumberMarker)
            throws Ks3ClientException, Ks3ServiceException {
        ListPartsRequest request = new ListPartsRequest(bucketName, objectKey,
                uploadId, maxParts, partNumberMarker);
        return this.syncListParts(request);
    }

    @Override
    public ListPartsResult syncListParts(ListPartsRequest request)
            throws Ks3ClientException, Ks3ServiceException {

        final Ks3SyncResult<ListPartsResult> syncResult = new Ks3SyncResult<>();
        this.listParts(request, new ListPartsResponseHandler() {

            @Override
            public void onSuccess(ListPartsRequest request, ListPartsResult result) {
                syncResult.setResult(result);
            }

            @Override
            public void onFailure(ListPartsRequest request, Ks3ClientException clientException, Ks3ServiceException serviceException) {
                syncResult.setClientException(clientException);
                syncResult.setServiceException(serviceException);
            }
        }, false);

        if (syncResult.getClientException() != null) {
            throw syncResult.getClientException();
        }

        if (syncResult.getServiceException() != null) {
            throw syncResult.getServiceException();
        }

        return syncResult.getResult();
    }

    @Override
    public GetObjectResult syncGetObject(GetObjectRequest request, File file,
                                         boolean append) throws Ks3ClientException, Ks3ServiceException {

        final Ks3SyncResult<GetObjectResult> syncResult = new Ks3SyncResult<>();
        this.getObject(request, new GetObjectResponseHandler(file, append) {
            @Override
            public void onTaskProgress(double progress) {

            }

            @Override
            public void onSuccess(GetObjectRequest request, GetObjectResult result) {
                syncResult.setResult(result);
            }

            @Override
            public void onTaskCancel() {

            }

            @Override
            public void onFailure(GetObjectRequest request, Ks3ClientException clientException, Ks3ServiceException serviceException) {
                syncResult.setClientException(clientException);
                syncResult.setServiceException(serviceException);
            }
        }, false);

        if (syncResult.getClientException() != null) {
            throw syncResult.getClientException();
        }

        if (syncResult.getServiceException() != null) {
            throw syncResult.getServiceException();
        }

        return syncResult.getResult();
    }

    @Override
    public GetObjectResult syncGetObject(Context context, File file, boolean append,
                                         String bucketname, String key,
                                         GetObjectResponseHandler getObjectResponceHandler) throws Ks3ClientException, Ks3ServiceException {
        GetObjectRequest request = new GetObjectRequest(bucketname, key);
        return this.syncGetObject(request, file, append);
    }

    @Override
    public PutObjectResult syncPutObject(String bucketname, String objectkey, File file)
            throws Ks3ClientException, Ks3ServiceException {
        PutObjectRequest request = new PutObjectRequest(bucketname, objectkey,
                file);
        return this.syncPutObject(request);
    }

    @Override
    public PutObjectResult syncPutObject(String bucketname, String objectkey, File file,
                                         ObjectMetadata objectmeta) throws Ks3ClientException, Ks3ServiceException {
        PutObjectRequest request = new PutObjectRequest(bucketname, objectkey,
                file, objectmeta);
        return this.syncPutObject(request);
    }

    @Override
    public PutObjectResult syncPutObject(PutObjectRequest request) throws Ks3ClientException, Ks3ServiceException {
        final Ks3SyncResult<PutObjectResult> syncResult = new Ks3SyncResult<>();
        this.putObject(request, new PutObjectResponseHandler() {

            @Override
            public void onTaskSuccess(PutObjectRequest request, PutObjectResult result) {
                syncResult.setResult(result);
            }

            @Override
            public void onTaskProgress(double progress) {

            }

            @Override
            public void onTaskFailure(PutObjectRequest request, Ks3ClientException clientException, Ks3ServiceException serviceException) {
                syncResult.setClientException(clientException);
                syncResult.setServiceException(serviceException);
            }

            @Override
            public void onTaskCancel() {

            }
        }, false);

        if (syncResult.getClientException() != null) {
            throw syncResult.getClientException();
        }

        if (syncResult.getServiceException() != null) {
            throw syncResult.getServiceException();
        }

        return syncResult.getResult();
    }

    @Override
    public UploadPartResult syncUploadPart(String bucketName, String key,
                                           String uploadId, File file, long offset, int partNumber,
                                           long partSize) throws Ks3ClientException, Ks3ServiceException {
        UploadPartRequest request = new UploadPartRequest(bucketName, key,
                uploadId, file, offset, partNumber, partSize);
        return this.syncUploadPart(request);
    }

    @Override
    public UploadPartResult syncUploadPart(UploadPartRequest request) throws Ks3ClientException, Ks3ServiceException {
        final Ks3SyncResult<UploadPartResult> syncResult = new Ks3SyncResult<>();
        this.uploadPart(request, new UploadPartResponseHandler() {

            @Override
            public void onSuccess(UploadPartRequest request, UploadPartResult result) {
                syncResult.setResult(result);
            }

            @Override
            public void onFailure(UploadPartRequest request, Ks3ClientException clientException, Ks3ServiceException serviceException) {
                syncResult.setClientException(clientException);
                syncResult.setServiceException(serviceException);
            }

            @Override
            public void onTaskProgress(double progress) {

            }
        }, false);

        if (syncResult.getClientException() != null) {
            throw syncResult.getClientException();
        }

        if (syncResult.getServiceException() != null) {
            throw syncResult.getServiceException();
        }

        return syncResult.getResult();
    }
}
